#include <QJsonObject>
#include <QJsonArray>
#include <QMap>

#include "Ent.h"
#include "JsonReader.h"
#include "QtOSGWidget.h"

float g_alpha = 1.0f;
osg::Vec4f g_selectedFaceColor = osg::Vec4f(1.0f, 1.0f, 0.f, g_alpha);
osg::Vec4f g_faceColor = osg::Vec4f(0xA0 / 255.0f, 0xA0 / 255.0f, 0xA0 / 255.0f, g_alpha);

void setAlpha(float alpha)
{
	if (alpha < 0.3f)
		alpha = 0.3f;
	//else if (alpha > 1.0f)
	//	alpha = 1.0f;
	if (alpha == g_alpha)
		return;
	g_alpha = alpha;
	g_faceColor.a() = g_alpha;
	g_selectedFaceColor.a() = g_alpha;
	//addFlags(UPDATE_GEOMETRY);
}

void rotate2D(const double x, const double y, const double angle, osg::Vec3d& point)
{
	double detX = point.x() - x;
	double detY = point.y() - y;
	double cosAngle = cos(angle);
	double sinAngle = sin(angle);
	double detX2 = detX * cosAngle - detY * sinAngle;
	double detY2 = detX * sinAngle + detY * cosAngle;
	point.set(x + detX2, y + detY2, point.z());
}

osg::Matrixd rotate2DMatrix(const double x, const double y, const double angle)
{
	osg::Matrixd t1;
	t1.makeTranslate(-x, -y, 0);
	osg::Matrixd r;
	r.makeRotate(angle, osg::Vec3d(0, 0, 1));
	osg::Matrixd t2;
	t2.makeTranslate(x, y, 0);
	return t1 * r * t2;
}

osg::Vec3d mirror2D(const osg::Vec3d& start, const osg::Vec3d& end, const osg::Vec3d& original)
{
	osg::Vec3d mirror;
	double t;
	double dx = end.x() - start.x();
	double dy = end.y() - start.y();
	if (dx == 0.0 && dy == 0.0)
		t = 0.0;
	else
	{
		double lineLength = dx * dx + dy * dy;
		double distance = (original.x() - start.x()) * dx + (original.y() - start.y()) * dy;
		t = distance / lineLength;
	}
	osg::Vec3d midPoint(start.x() + t * dx, start.y() + t * dy, 0);
	mirror.set(2.0*midPoint.x() - original.x(), 2.0*midPoint.y() - original.y(), 0);
	return mirror;
}

Ent::Ent(INode* parent)
	: INode(parent)
{
}

std::shared_ptr<Ent> Ent::clone() const
{
	return nullptr;
}

void Ent::save(QJsonObject & json) const
{
	json["class"] = metaObject()->className();
	json["id"] = getID();
}

void Ent::load(const QJsonObject & json, JsonReader & reader)
{
	int oldId = json["id"].toInt();
	reader.idMap.insert(std::make_pair(oldId, getID()));
}

void Ent::move(const osg::Vec3d & offset)
{
}

void Ent::scale(const osg::Vec3d & center, double scale)
{
}

std::vector<osg::Vec3d> Ent::getAllWCSHandles() const
{
	std::vector<osg::Vec3d> result;
	if (!isLeaf())
	{
		visitChildren([&result](const std::shared_ptr<INode>& node) {
			if (node->isLeaf())
			{
				auto stretchable = dynamic_cast<Ent*>(node.get());
				if (!stretchable)
					return;
				auto osgNode = stretchable->getRep3D();
				auto matrix = osgNode->getWorldMatrices()[0]/* * VPW*/;
				auto points = stretchable->handles();
				for (size_t i = 0; i < points.size(); ++i)
				{
					auto pt = points[i] * matrix;
					result.push_back(pt);
				}
			}
		}, true);
	}
	else
	{
		auto osgNode = getRep3D();
		auto matrix = osgNode->getWorldMatrices()[0]/* * VPW*/;
		auto points = handles();
		for (size_t i = 0; i < points.size(); ++i)
		{
			auto pt = points[i] * matrix;
			result.push_back(pt);
		}
	}
	return result;
}

std::vector<osg::Vec3d> Ent::handles() const
{
	return std::vector<osg::Vec3d>();
}

void Ent::stretch(int index, const osg::Vec3d & offset)
{
}

bool Ent::explode(std::vector<std::shared_ptr<Ent>>& result) const
{
	return false;
}

void Ent::copy(const Ent& ent)
{
}

void Ent::updateColor()
{
}

void Ent::rotate(const double angle, const osg::Vec2d & basePoint)
{
}


Ent2D::Ent2D(INode * parent)
	: Ent(parent)
{
}


void Ent2D::setColor(const osg::Vec4 & color)
{
}

Ent2DCurve::Ent2DCurve(INode * parent)
	: Ent2D(parent)
{
}

std::vector<Handle(Geom2d_Curve)> Ent2DCurve::getCurve2d() const
{
	return std::vector<Handle(Geom2d_Curve)>();
}

std::shared_ptr<Ent2DCurve> Ent2DCurve::extend(bool front, double param, const gp_Pnt2d& point) const
{
	return std::shared_ptr<Ent2DCurve>();
}

std::vector<std::shared_ptr<Ent2DCurve>> Ent2DCurve::trim(const SectionParam& from, const SectionParam& to) const
{
	return std::vector<std::shared_ptr<Ent2DCurve>>();
}

bool Ent2DCurve::offset(double offset)
{
	return false;
}

bool Ent2DCurve::offset(double offset, const osg::Vec3d & offsetSide)
{
	return false;
}

std::vector<std::shared_ptr<Ent3DCurve>> Ent2DCurve::to3d()
{
	return std::vector<std::shared_ptr<Ent3DCurve>>();
}

void Ent3DCurve::setColor(const osg::Vec4 & color)
{
}

Ent3DCurve::Ent3DCurve(INode * parent)
	: Ent3D(parent)
{
}

std::vector<Handle(Geom_Curve)> Ent3DCurve::getCurve3d() const
{
	return std::vector<Handle(Geom_Curve)>();
}

Ent3DFace::Ent3DFace(INode * parent)
	: Ent3D(parent)
{
}

std::vector<std::vector<osg::Vec3d>> Ent3DFace::getWCSEdgePoints() const
{
	auto edges = getEdgePoints();
	if (edges.empty())
		return edges;
	auto osgNode = this->getRep3D();
	auto matrix = osgNode->getWorldMatrices()[0]/* * VPW*/;
	for (auto& edge : edges)
	{
		for (size_t i = 0; i < edge.size(); ++i)
		{
			edge[i] = edge[i] * matrix;
		}
	}
	return edges;
}

std::vector<std::vector<osg::Vec3d>> Ent3DFace::getEdgePoints() const
{
	return std::vector<std::vector<osg::Vec3d>>();
}

void Ent3DFace::reverse()
{
}

std::vector<std::shared_ptr<Ent>> convert(const std::vector<std::shared_ptr<Ent2D>>& con)
{
	std::vector<std::shared_ptr<Ent>> result;
	for (auto e : con)
	{
		result.push_back(e);
	}
	return result;
}

std::set<std::shared_ptr<INode>> convertToSet(const std::vector<std::shared_ptr<Ent2D>>& con)
{
	std::set<std::shared_ptr<INode>> result;
	for (auto e : con)
	{
		result.insert(e);
	}
	return result;
}

std::map<std::string, CreateFunc>& getEntCreators()
{
	static std::map<std::string, CreateFunc> g_entCreators;
	return g_entCreators;
}
bool regEntClass(const std::string & className, CreateFunc creator)
{
	auto& g_entCreators = getEntCreators();
	auto it = g_entCreators.find(className);
	if (it == g_entCreators.end())
	{
		g_entCreators.insert({ className, creator });
		return true;
	}
	return false;
}

std::shared_ptr<Ent> createInstance(const std::string & className, INode * parent)
{
	auto& g_entCreators = getEntCreators();
	auto it = g_entCreators.find(className);
	if (it == g_entCreators.end())
		return std::shared_ptr<Ent>();
	return it->second(parent);
}

/////////////////////////////////////////////
#include "Ucs.h"
#include "LineStrip2D.h"
#include "LineStrip3D.h"
#include "Arc2D.h"
#include "Arc3D.h"
#include "Pline2D.h"
#include "Cylinder.h"
#include "MultiSectionCylinder.h"
#include "Spline2D.h"
#include "Spline3D.h"
#include "Surface.h"
#include "Polygon.h"

template<typename T>
std::shared_ptr<Ent> createEnt(INode* parent)
{
	return std::make_shared<T>(parent);
}

template<typename T>
bool regEntTpl()
{
	bool registerThisEnt = regEntClass(T::staticMetaObject.className(), createEnt<T>);
	return registerThisEnt;
}

void regAllEnts()
{
	regEntTpl<UCS>();
	regEntTpl<LineStrip2D>();
	regEntTpl<LineStrip3D>();
	regEntTpl<Pline2D>();
	regEntTpl<Arc2D>();
	regEntTpl<Arc3D>();
	regEntTpl<Cylinder>();
	regEntTpl<MultiSectionCylinder>();
	regEntTpl<Spline2D>();
	regEntTpl<Spline3D>();
	regEntTpl<Surface>();
	regEntTpl<Polygon>();
}

Ent3D::Ent3D(INode * parent)
	: Ent(parent)
{
}
